home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CICA Windows Explosion!
/
The CICA Windows Explosion! - Disc 2.iso
/
nt
/
gr564s.zip
/
SRC
/
MAKETIME.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-09-05
|
8KB
|
322 lines
/* maketime - yield time_t from struct tm yielded by partime */
/* Copyright 1992 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#include "rcsbase.h"
libId(maketId, "$Id: maketime.c,v 5.7 1992/05/31 08:29:18 eggert Exp $")
/*
* For maximum portability, use only localtime and gmtime.
* Make no assumptions about the epoch or the range of time_t values.
* Avoid mktime because it's not universal and because there's no easy,
* portable way for mktime to yield the inverse of gmtime.
*/
#define TM_YEAR_ORIGIN 1900
#define LOCAL_TIME (48*60)
static int
isleap(y)
int y;
{
return (y&3) == 0 && (y%100 != 0 || y%400 == 0);
}
static int const month_yday[] = {
/* days in year before start of months 0-12 */
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
};
/* Yield the number of days in TM's month. */
static int
month_days(tm)
struct tm const *tm;
{
int m = tm->tm_mon;
return month_yday[m+1] - month_yday[m]
+ (m==1 && isleap(tm->tm_year + TM_YEAR_ORIGIN));
}
/*
* Convert UNIXTIME to struct tm form.
* Use gmtime if available and if !LOCALZONE, localtime otherwise.
*/
static struct tm *
time2tm(unixtime, localzone)
time_t unixtime;
int localzone;
{
struct tm *tm;
# if TZ_must_be_set
static char const *TZ;
if (!TZ && !(TZ = getenv("TZ")))
faterror("TZ is not set");
# endif
if (localzone || !(tm = gmtime(&unixtime)))
tm = localtime(&unixtime);
return tm;
}
/* Yield A - B, measured in seconds. */
static time_t
difftm(a, b)
struct tm const *a, *b;
{
int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
return
(
(
(
/* difference in day of year */
a->tm_yday - b->tm_yday
/* + intervening leap days */
+ ((ay >> 2) - (by >> 2))
- (ay/100 - by/100)
+ ((ay/100 >> 2) - (by/100 >> 2))
/* + difference in years * 365 */
+ (time_t)(ay-by) * 365
)*24 + (a->tm_hour - b->tm_hour)
)*60 + (a->tm_min - b->tm_min)
)*60 + (a->tm_sec - b->tm_sec);
}
/*
* Adjust T by adding MINUTES. MINUTES must be at most 24 hours' worth.
* Adjust only T's year, mon, mday, hour and min members;
* plus adjust wday if it is not negative.
*/
static void
adjzone(t, minutes)
register struct tm *t;
int minutes;
{
if ((t->tm_min += minutes) < 0) {
if ((t->tm_hour -= (59-t->tm_min)/60) < 0) {
t->tm_hour += 24;
if (0 <= t->tm_wday && --t->tm_wday < 0)
t->tm_wday = 6;
if (--t->tm_mday <= 0) {
if (--t->tm_mon < 0) {
--t->tm_year;
t->tm_mon = 11;
}
t->tm_mday = month_days(t);
}
}
t->tm_min += 24*60;
} else
if (24 <= (t->tm_hour += t->tm_min/60)) {
t->tm_hour -= 24;
if (0 <= t->tm_wday && ++t->tm_wday == 7)
t->tm_wday = 0;
if (month_days(t) < ++t->tm_mday) {
if (11 < ++t->tm_mon) {
++t->tm_year;
t->tm_mon = 0;
}
t->tm_mday = 1;
}
}
t->tm_min %= 60;
}
/*
* Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise.
* Use only TM's year, mon, mday, hour, min, and sec members.
* Ignore TM's old tm_yday and tm_wday, but fill in their correct values.
* Yield -1 on failure (e.g. a member out of range).
* Posix 1003.1-1990 doesn't allow leap seconds, but some implementations
* have them anyway, so allow them if localtime/gmtime does.
*/
static time_t
tm2time(tm, localzone)
struct tm *tm;
int localzone;
{
/* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime. */
static time_t t_cache[2];
static struct tm tm_cache[2];
time_t d, gt;
struct tm const *gtm;
/*
* The maximum number of iterations should be enough to handle any
* combinations of leap seconds, time zone rule changes, and solar time.
* 4 is probably enough; we use a bigger number just to be safe.
*/
int remaining_tries = 8;
/* Avoid subscript errors. */
if (12 <= (unsigned)tm->tm_mon)
return -1;
tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday
- (tm->tm_mon<2 || ! isleap(tm->tm_year + TM_YEAR_ORIGIN));
/* Make a first guess. */
gt = t_cache[localzone];
gtm = gt ? &tm_cache[localzone] : time2tm(gt,localzone);
/* Repeatedly use the error from the guess to improve the guess. */
while ((d = difftm(tm, gtm)) != 0) {
if (--remaining_tries == 0)
return -1;
gt += d;
gtm = time2tm(gt,localzone);
}
t_cache[localzone] = gt;
tm_cache[localzone] = *gtm;
/*
* Check that the guess actually matches;
* overflow can cause difftm to yield 0 even on differing times,
* or tm may have members out of range (e.g. bad leap seconds).
*/
if ( tm->tm_year ^ gtm->tm_year
| tm->tm_mon ^ gtm->tm_mon
| tm->tm_mday ^ gtm->tm_mday
| tm->tm_hour ^ gtm->tm_hour
| tm->tm_min ^ gtm->tm_min
| tm->tm_sec ^ gtm->tm_sec)
return -1;
tm->tm_wday = gtm->tm_wday;
return gt;
}
/*
* Check TM0 and convert it to time_t, assuming ZONE minutes west of gmtime.
* Use localtime if ZONE is the special value LOCAL_TIME.
* Ignore TM0->tm_yday; assume default values for any other negative inputs.
* Yield -1 on failure.
*/
static time_t
maketime(tm0, zone)
struct tm const *tm0;
int zone;
{
int localzone, wday;
struct tm tm;
time_t r;
localzone = zone==LOCAL_TIME;
tm = *tm0;
if (tm.tm_year < 0) {
/* Set default year, month, day from current time. */
register struct tm *d = time2tm(now(), localzone);
if (!localzone)
adjzone(d, -zone);
tm.tm_year = d->tm_year;
if (tm.tm_mon < 0) {
tm.tm_mon = d->tm_mon;
if (tm.tm_mday < 0)
tm.tm_mday = d->tm_mday;
}
}
/* Set remaining default fields to be their minimum values. */
if (tm.tm_mon < 0) tm.tm_mon = 0;
if (tm.tm_mday < 0) tm.tm_mday = 1;
if (tm.tm_hour < 0) tm.tm_hour = 0;
if (tm.tm_min < 0) tm.tm_min = 0;
if (tm.tm_sec < 0) tm.tm_sec = 0;
if (!localzone)
adjzone(&tm, zone);
wday = tm.tm_wday;
/* Convert and fill in the rest of the tm. */
r = tm2time(&tm, localzone);
/* Check weekday. */
if (r != -1 && 0 <= wday && wday != tm.tm_wday)
return -1;
return r;
}
/*
* Convert Unix time to RCS format.
* For compatibility with older versions of RCS,
* dates before AD 2000 are stored without the leading "19".
*/
void
time2date(unixtime,date)
time_t unixtime;
char date[datesize];
{
register struct tm const *tm = time2tm(unixtime, RCSversion<VERSION(5));
VOID sprintf(date, DATEFORM,
tm->tm_year + (tm->tm_year<100 ? 0 : TM_YEAR_ORIGIN),
tm->tm_mon+1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec
);
}
/* Parse a free-format date in SOURCE, yielding a Unix format time. */
static time_t
str2time(source)
char const *source;
{
int zone;
time_t unixtime;
struct tm parseddate;
if (!partime(source, &parseddate, &zone))
faterror("can't parse date/time: %s", source);
if (LOCAL_TIME < zone)
/* No zone was specified. */
zone = RCSversion<VERSION(5) ? LOCAL_TIME : 0;
if ((unixtime = maketime(&parseddate, zone)) == -1)
faterror("bad date/time: %s", source);
return unixtime;
}
/*
* Parse a free-format date in SOURCE, convert it
* into RCS internal format, and store the result into TARGET.
*/
void
str2date(source, target)
char const *source;
char target[datesize];
{
time2date(str2time(source), target);
}
/* Convert an RCS internal format date to time_t. */
time_t
date2time(source)
char const source[datesize];
{
char s[datesize];
return str2time(date2str(source, s));
}